#include <iostream>
#include <cstring>
#include <chrono>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include "cmodel.h"
#include "homo_block.h"
#include <fstream>
#include <regex>
#include "hscript_engine.hpp"
#include <streambuf>

#include <boost/program_options.hpp>

#ifndef BYTELEN
#define BYTELEN 8
#endif
#ifndef FOURBITS
#define FOURBITS(x) (x & 0xf)
#endif
#ifndef BYTECOUNT
#define BYTECOUNT(bitcount) ((bitcount + BYTELEN - 1) / BYTELEN)
#endif
#ifndef MON_MUL_BLOCK_LENGTH
#define MON_MUL_BLOCK_LENGTH 128
#endif

using namespace std;

void generate_test_parameter(const char opcode, const int count, const int p_bitcount, char *output) {
  BIGNUM *p = BN_new();
  BIGNUM *temp = BN_new();
  BN_rand(p, p_bitcount, 0, 1);
  int offset = 0;
  BN_bn2binpad(p, (unsigned char*)(output + offset), BYTECOUNT(p_bitcount));
  offset += BYTECOUNT(p_bitcount);
  if (FOURBITS(opcode) == 0) {  // 操作码为0，代表向量模加
    for (int i = 0; i < 2 * count; i++) {
      BN_rand_range(temp, p);
      BN_bn2binpad(temp, (unsigned char*)(output + offset), BYTECOUNT(p_bitcount));
      offset += BYTECOUNT(p_bitcount);
    }
    BN_free(p);
    BN_free(temp);
    return;
  }

  BIGNUM *n_prime = BN_new();
  BIGNUM *r_square = BN_new();
  BN_CTX *bn_ctx = BN_CTX_new();
  // compute n_prime = -p^(-1) mod 2^block_bits
  BN_lshift(r_square, BN_value_one(), MON_MUL_BLOCK_LENGTH);
  BN_mod_inverse(n_prime, p, r_square, bn_ctx);
  BN_zero(temp);
  BN_sub(n_prime, temp, n_prime);
  BN_nnmod(n_prime, n_prime, r_square, bn_ctx);
  // compute 2^(2 * (n + block_bits)) mod p
  BN_lshift(r_square, BN_value_one(), 2 * (p_bitcount + MON_MUL_BLOCK_LENGTH));
  BN_mod(r_square, r_square, p, bn_ctx);
  BN_bn2binpad(n_prime, (unsigned char*)(output + offset), BYTECOUNT(MON_MUL_BLOCK_LENGTH));
  offset += BYTECOUNT(MON_MUL_BLOCK_LENGTH);
  BN_bn2binpad(r_square, (unsigned char*)(output + offset), BYTECOUNT(p_bitcount));
  offset += BYTECOUNT(p_bitcount);

  if (FOURBITS(opcode) == 1) {  // 操作码为1，代表向量模乘
    for (int i = 0; i < 2 * count; i++) {
      BN_rand_range(temp, p);
      BN_bn2binpad(temp, (unsigned char*)(output + offset), BYTECOUNT(p_bitcount));
      offset += BYTECOUNT(p_bitcount);
    }
  } else if (FOURBITS(opcode) == 2) {  // 操作码为2，代表向量模乘，不过第二个乘数是个常量而非向量
    for (int i = 0; i <= count; i++) {
      BN_rand_range(temp, p);
      BN_bn2binpad(temp, (unsigned char*)(output + offset), BYTECOUNT(p_bitcount));
      offset += BYTECOUNT(p_bitcount);
    }
  } else if (FOURBITS(opcode) == 3) {  // 操作码为3，代表向量模幂
    for (int i = 0; i < count; i++) {
      BN_rand_range(temp, p);
      BN_bn2binpad(temp, (unsigned char*)(output + offset), BYTECOUNT(p_bitcount));
      offset += BYTECOUNT(p_bitcount);
    }
    short int e_bitcount = p_bitcount;
    memcpy(output + offset, reinterpret_cast<char*>(&e_bitcount), sizeof(short int));
    offset += sizeof(short int);
    for (int i = 0; i < count; i++) {
      BN_rand_range(temp, p);
      BN_bn2binpad(temp, (unsigned char*)(output + offset), BYTECOUNT(e_bitcount));
      offset += BYTECOUNT(e_bitcount);
    }
  } else if (FOURBITS(opcode) == 4) {  // 操作码为4，代表向量模幂，不过底数是个常量而非向量
    BN_rand_range(temp, p);
    BN_bn2binpad(temp, (unsigned char*)(output + offset), BYTECOUNT(p_bitcount));
    offset += BYTECOUNT(p_bitcount);
    short int e_bitcount = p_bitcount;
    memcpy(output + offset, reinterpret_cast<char*>(&e_bitcount), sizeof(short int));
    offset += sizeof(short int);
    for (int i = 0; i < count; i++) {
      BN_rand_range(temp, p);
      BN_bn2binpad(temp, (unsigned char*)(output + offset), BYTECOUNT(e_bitcount));
      offset += BYTECOUNT(e_bitcount);
    }
  } else if (FOURBITS(opcode) == 5) {  // 操作码为5，代表同态乘法点积
    for (int i = 0; i < 2 * count; i++) {
      BN_rand_range(temp, p);
      BN_bn2binpad(temp, (unsigned char*)(output + offset), BYTECOUNT(p_bitcount));
      offset += BYTECOUNT(p_bitcount);
    }
  } else if (FOURBITS(opcode) == 6) {  // 操作码为6，代表同态指数点积
    for (int i = 0; i < count; i++) {
      BN_rand_range(temp, p);
      BN_bn2binpad(temp, (unsigned char*)(output + offset), BYTECOUNT(p_bitcount));
      offset += BYTECOUNT(p_bitcount);
    }
    short int e_bitcount = p_bitcount;
    memcpy(output + offset, reinterpret_cast<char*>(&e_bitcount), sizeof(short int));
    offset += sizeof(short int);
    for (int i = 0; i < count; i++) {
      BN_rand_range(temp, p);
      BN_bn2binpad(temp, (unsigned char*)(output + offset), BYTECOUNT(e_bitcount));
      offset += BYTECOUNT(e_bitcount);
    }
  } else {
    std::cout << "wrong operation code " << int(FOURBITS(opcode)) << std::endl;
  }

  BN_free(p);
  BN_free(n_prime);
  BN_free(r_square);
  BN_free(temp);
  BN_CTX_free(bn_ctx);
  return;
}

void compute_with_openssl(const char opcode, const int count, const int p_bitcount, const char *input, char *output) {
  int offset = 0;
  BN_CTX *bn_ctx = BN_CTX_new();
  // 读出模数p的值
  BIGNUM *p_bn = BN_new();
  BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), p_bn);
  offset += BYTECOUNT(p_bitcount);

  BIGNUM **a = new BIGNUM*[count];
  BIGNUM **b = new BIGNUM*[count];
  BIGNUM **result = new BIGNUM*[count];
  for (int i = 0; i < count; i++) {
    a[i] = BN_new();
    b[i] = BN_new();
    result[i] = BN_new();
  }

  if (FOURBITS(opcode) == 0) {  // 操作码为0，代表向量模加
    for (int i = 0; i < count; i++) {
      BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), a[i]);
      offset += BYTECOUNT(p_bitcount);
    }
    for (int i = 0; i < count; i++) {
      BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), b[i]);
      offset += BYTECOUNT(p_bitcount);
    }
    for (int i = 0; i < count; i++) {
      BN_mod_add(result[i], a[i], b[i], p_bn, bn_ctx);
      BN_bn2binpad(result[i], (unsigned char*)(output + i * BYTECOUNT(p_bitcount)), BYTECOUNT(p_bitcount));
    }
  }
  else {
    // 读出蒙哥马利计算时需要的n'的值
    BIGNUM *n_prime_bn = BN_new();
    BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(MON_MUL_BLOCK_LENGTH), n_prime_bn);
    offset += BYTECOUNT(MON_MUL_BLOCK_LENGTH);
    // 读出蒙哥马利计算时需要的r^2的值
    BIGNUM *r_square_bn = BN_new();
    BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), r_square_bn);
    offset += BYTECOUNT(p_bitcount);
    // 以上变量是所有操作都会输入的

    if (FOURBITS(opcode) == 1) {  // 操作码为1，代表向量模乘
      for (int i = 0; i < count; i++) {
        BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), a[i]);
        offset += BYTECOUNT(p_bitcount);
      }
      for (int i = 0; i < count; i++) {
        BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), b[i]);
        offset += BYTECOUNT(p_bitcount);
      }
      for (int i = 0; i < count; i++) {
        BN_mod_mul(result[i], a[i], b[i], p_bn, bn_ctx);
        BN_bn2binpad(result[i], (unsigned char*)(output + i * BYTECOUNT(p_bitcount)), BYTECOUNT(p_bitcount));
      }
    } else if (FOURBITS(opcode) == 2) {  // 操作码为2，代表向量模乘，不过第二个乘数是个常量而非向量
      for (int i = 0; i < count; i++) {
        BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), a[i]);
        offset += BYTECOUNT(p_bitcount);
      }
      BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), b[0]);
      offset += BYTECOUNT(p_bitcount);
      for (int i = 0; i < count; i++) {
        BN_mod_mul(result[i], a[i], b[0], p_bn, bn_ctx);
        BN_bn2binpad(result[i], (unsigned char*)(output + i * BYTECOUNT(p_bitcount)), BYTECOUNT(p_bitcount));
      }
    } else if (FOURBITS(opcode) ==3) {  // 操作码为3，代表向量模幂
      for (int i = 0; i < count; i++) {
        BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), a[i]);
        offset += BYTECOUNT(p_bitcount);
      }
      offset += sizeof(short int);
      for (int i = 0; i < count; i++) {
        BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), b[i]);
        offset += BYTECOUNT(p_bitcount);
      }
      for (int i = 0; i < count; i++) {
        BN_mod_exp(result[i], a[i], b[i], p_bn, bn_ctx);
        BN_bn2binpad(result[i], (unsigned char*)(output + i * BYTECOUNT(p_bitcount)), BYTECOUNT(p_bitcount));
      }
    } else if (FOURBITS(opcode) == 4) {  // 操作码为4，代表向量模幂，不过底数是个常量而非向量
      BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), a[0]);
      offset += BYTECOUNT(p_bitcount);
      offset += sizeof(short int);
      for (int i = 0; i < count; i++) {
        BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), b[i]);
        offset += BYTECOUNT(p_bitcount);
      }
      for (int i = 0; i < count; i++) {
        BN_mod_exp(result[i], a[0], b[i], p_bn, bn_ctx);
        BN_bn2binpad(result[i], (unsigned char*)(output + i * BYTECOUNT(p_bitcount)), BYTECOUNT(p_bitcount));
      }
    } else if (FOURBITS(opcode) == 5) {  // 操作码为5，代表同态乘法点积
      for (int i = 0; i < count; i++) {
        BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), a[i]);
        offset += BYTECOUNT(p_bitcount);
      }
      for (int i = 0; i < count; i++) {
        BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), b[i]);
        offset += BYTECOUNT(p_bitcount);
      }
      for (int i = 0; i < count; i++) {
        BN_mod_mul(result[i], a[i], b[i], p_bn, bn_ctx);
      }
      for (int i = 1; i < count; i++) {
        BN_mod_add(result[0], result[0], result[i], p_bn, bn_ctx);
      }
      BN_bn2binpad(result[0], (unsigned char*)(output), BYTECOUNT(p_bitcount));
    } else if (FOURBITS(opcode) == 6) {  // 操作码为6，代表同态指数点积
      for (int i = 0; i < count; i++) {
        BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), a[i]);
        offset += BYTECOUNT(p_bitcount);
      }
      offset += sizeof(short int);
      for (int i = 0; i < count; i++) {
        BN_bin2bn((unsigned char*)(input + offset), BYTECOUNT(p_bitcount), b[i]);
        offset += BYTECOUNT(p_bitcount);
      }
      for (int i = 0; i < count; i++) {
        BN_mod_exp(result[i], a[i], b[i], p_bn, bn_ctx);
      }
      for (int i = 1; i < count; i++) {
        BN_mod_mul(result[0], result[0], result[i], p_bn, bn_ctx);
      }
      BN_bn2binpad(result[0], (unsigned char*)(output), BYTECOUNT(p_bitcount));
    } else {
      std::cout << "wrong operation code " << int(FOURBITS(opcode)) << std::endl;
    }

    BN_free(n_prime_bn);
    BN_free(r_square_bn);
  }

  for (int i = 0; i < count; i++) {
    BN_free(a[i]);
    BN_free(b[i]);
    BN_free(result[i]);
  }
  delete[] a;
  delete[] b;
  delete[] result;
  BN_CTX_free(bn_ctx);
  BN_free(p_bn);
  return;
}

//void test_checksum() {
//  unsigned char *data1 = new unsigned char[11];
//  unsigned char *data2 = new unsigned char[32];
//  unsigned char *data3 = new unsigned char[1000];
//  unsigned char *data4 = new unsigned char[100001];
//  // set the first 16 bits as checksum part
//  for (int i = 0; i < 2; i++) {
//    data1[i] = 0;
//    data2[i] = 0;
//    data3[i] = 0;
//    data4[i] = 0;
//  }
//  for (int i = 2; i < 100001; i++) {
//    if (i < 11) {
//      data1[i] = i - 1;
//    }
//    if (i < 32) {
//      data2[i] = i - 1;
//    }
//    if (i < 1000) {
//      data3[i] = i - 1;
//    }
//    if (i <100001) {
//      data4[i] = i - 1;
//    }
//  }
//  if (checksum(data1, 11) != 0xebe6) {
//    std::cout <<"checksum wrong" << std::endl;
//  }
//  else if (checksum(data2, 32) != 0x0f1e) {
//    std::cout <<"checksum wrong" << std::endl;
//  }
//  else if (checksum(data3, 1000) != 0x6f63) {
//    std::cout <<"checksum wrong" << std::endl;
//  }
//  else if (checksum(data4, 100001) != 0x55c8) {
//    std::cout <<"checksum wrong" << std::endl;
//  }
//  else {
//    std::cout <<"test checksum successfully" << std::endl;
//  }
//  std::cout << std::endl;
//}

void test_mod_add(int count, int p_bitcount) {
  int input_size = (2 * count + 1) * BYTECOUNT(p_bitcount);
  int output_size = BYTECOUNT(p_bitcount) * count;
  char opcode = 0;
  char *input = new char[input_size];
  generate_test_parameter(opcode, count, p_bitcount, input);

  char *output_openssl = new char[output_size];
  auto start_openssl = std::chrono::system_clock::now();
  compute_with_openssl(opcode, count, p_bitcount, input, output_openssl);
  auto end_openssl = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_openssl = end_openssl - start_openssl;
  std::cout << "elapsed mod add of openssl time is " << diff_openssl.count() << " s" << std::endl;

  char *output;
  auto start_cmodel = std::chrono::system_clock::now();
  operation(opcode, count, p_bitcount, input, &output);
  auto end_cmodel = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_cmodel = end_cmodel - start_cmodel;
  std::cout << "elapsed mod add of cmodel time is " << diff_cmodel.count() << " s" << std::endl;
  char *output_cmodel = new char[output_size];
  memcpy(output_cmodel, input + BYTECOUNT(p_bitcount), output_size);

  int flag = 1;
  for (int i = 0; i < output_size; i++) {
    if (output_openssl[i] != output_cmodel[i]) {
      std::cout << "mod add result is wrong!" << std::endl;
      flag = 0;
      break;
    }
  }
  if (flag == 1) {
    std::cout << "mod add result is correct!" << std::endl;
  }
  std::cout << std::endl;
  delete[] input;
  delete[] output_openssl;
  delete[] output_cmodel;
  return;
}

void test_mod_mul(int count, int p_bitcount) {
  int input_size = (2 * count + 2) * BYTECOUNT(p_bitcount) + BYTECOUNT(MON_MUL_BLOCK_LENGTH);
  int output_size = BYTECOUNT(p_bitcount) * count;
  char opcode = 1;
  char *input = new char[input_size];
  generate_test_parameter(opcode, count, p_bitcount, input);

  char *output_openssl = new char[output_size];
  auto start_openssl = std::chrono::system_clock::now();
  compute_with_openssl(opcode, count, p_bitcount, input, output_openssl);
  auto end_openssl = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_openssl = end_openssl - start_openssl;
  std::cout << "elapsed mod mul of openssl time is " << diff_openssl.count() << " s" << std::endl;

  char *output;
  auto start_cmodel = std::chrono::system_clock::now();
  operation(opcode, count, p_bitcount, input, &output);
  auto end_cmodel = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_cmodel = end_cmodel - start_cmodel;
  std::cout << "elapsed mod mul of cmodel time is " << diff_cmodel.count() << " s" << std::endl;
  char *output_cmodel = new char[output_size];
  memcpy(output_cmodel, input + BYTECOUNT(p_bitcount) * 2 + BYTECOUNT(MON_MUL_BLOCK_LENGTH), output_size);

  int flag = 1;
  for (int i = 0; i < output_size; i++) {
    if (output_openssl[i] != output_cmodel[i]) {
      std::cout << "mod mul result is wrong!" << std::endl;
      flag = 0;
      break;
    }
  }
  if (flag == 1) {
    std::cout << "mod mul result is correct!" << std::endl;
  }
  std::cout << std::endl;
  delete[] input;
  delete[] output_openssl;
  delete[] output_cmodel;
  return;
}

void test_mod_mul_const(int count, int p_bitcount) {
  int input_size = (count + 3) * BYTECOUNT(p_bitcount) + BYTECOUNT(MON_MUL_BLOCK_LENGTH);
  int output_size = BYTECOUNT(p_bitcount) * count;
  char opcode = 2;
  char *input = new char[input_size];
  generate_test_parameter(opcode, count, p_bitcount, input);

  char *output_openssl = new char[output_size];
  auto start_openssl = std::chrono::system_clock::now();
  compute_with_openssl(opcode, count, p_bitcount, input, output_openssl);
  auto end_openssl = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_openssl = end_openssl - start_openssl;
  std::cout << "elapsed mod mul const of openssl time is " << diff_openssl.count() << " s" << std::endl;

  char *output;
  auto start_cmodel = std::chrono::system_clock::now();
  operation(opcode, count, p_bitcount, input, &output);
  auto end_cmodel = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_cmodel = end_cmodel - start_cmodel;
  std::cout << "elapsed mod mul const of cmodel time is " << diff_cmodel.count() << " s" << std::endl;
  char *output_cmodel = new char[output_size];
  memcpy(output_cmodel, input + BYTECOUNT(p_bitcount) * 2 + BYTECOUNT(MON_MUL_BLOCK_LENGTH), output_size);

  int flag = 1;
  for (int i = 0; i < output_size; i++) {
    if (output_openssl[i] != output_cmodel[i]) {
      std::cout << "mod mul const result is wrong!" << std::endl;
      flag = 0;
      break;
    }
  }
  if (flag == 1) {
    std::cout << "mod mul const result is correct!" << std::endl;
  }
  std::cout << std::endl;
  delete[] input;
  delete[] output_openssl;
  delete[] output_cmodel;
  return;
}

void test_mod_exp(int count, int p_bitcount) {
  int input_size = (2 * count + 2) * BYTECOUNT(p_bitcount) + BYTECOUNT(MON_MUL_BLOCK_LENGTH) + sizeof(short int);
  int output_size = BYTECOUNT(p_bitcount) * count;
  char opcode = 3;
  char *input = new char[input_size];
  generate_test_parameter(opcode, count, p_bitcount, input);

  char *output_openssl = new char[output_size];
  auto start_openssl = std::chrono::system_clock::now();
  compute_with_openssl(opcode, count, p_bitcount, input, output_openssl);
  auto end_openssl = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_openssl = end_openssl - start_openssl;
  std::cout << "elapsed mod exp of openssl time is " << diff_openssl.count() << " s" << std::endl;

  char *output;
  auto start_cmodel = std::chrono::system_clock::now();
  operation(opcode, count, p_bitcount, input, &output);
  auto end_cmodel = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_cmodel = end_cmodel - start_cmodel;
  std::cout << "elapsed mod exp of cmodel time is " << diff_cmodel.count() << " s" << std::endl;

  int flag = 1;
  for (int i = 0; i < output_size; i++) {
    if (output_openssl[i] != output[i]) {
      std::cout << "mod exp result is wrong!" << std::endl;
      flag = 0;
      break;
    }
  }
  if (flag == 1) {
    std::cout << "mod exp result is correct!" << std::endl;
  }
  std::cout << std::endl;
  delete[] input;
  delete[] output_openssl;
  return;
}

void test_mod_const_exp(int count, int p_bitcount) {
  int input_size = (count + 3) * BYTECOUNT(p_bitcount) + BYTECOUNT(MON_MUL_BLOCK_LENGTH) + sizeof(short int);
  int output_size = BYTECOUNT(p_bitcount) * count;
  char opcode = 4;
  char *input = new char[input_size];
  generate_test_parameter(opcode, count, p_bitcount, input);

  char *output_openssl = new char[output_size];
  auto start_openssl = std::chrono::system_clock::now();
  compute_with_openssl(opcode, count, p_bitcount, input, output_openssl);
  auto end_openssl = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_openssl = end_openssl - start_openssl;
  std::cout << "elapsed mod const exp of openssl time is " << diff_openssl.count() << " s" << std::endl;

  char *output;
  auto start_cmodel = std::chrono::system_clock::now();
  operation(opcode, count, p_bitcount, input, &output);
  auto end_cmodel = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_cmodel = end_cmodel - start_cmodel;
  std::cout << "elapsed mod const exp of cmodel time is " << diff_cmodel.count() << " s" << std::endl;

  int flag = 1;
  for (int i = 0; i < output_size; i++) {
    if (output_openssl[i] != output[i]) {
      std::cout << "mod const exp result is wrong!" << std::endl;
      flag = 0;
      break;
    }
  }
  if (flag == 1) {
    std::cout << "mod const exp result is correct!" << std::endl;
  }
  std::cout << std::endl;
  delete[] input;
  delete[] output_openssl;
  return;
}

void test_mod_dot(int count, int p_bitcount) {
  int input_size = (2 * count + 2) * BYTECOUNT(p_bitcount) + BYTECOUNT(MON_MUL_BLOCK_LENGTH);
  int output_size = BYTECOUNT(p_bitcount);
  char opcode = 5;
  char *input = new char[input_size];
  generate_test_parameter(opcode, count, p_bitcount, input);

  char *output_openssl = new char[output_size];
  auto start_openssl = std::chrono::system_clock::now();
  compute_with_openssl(opcode, count, p_bitcount, input, output_openssl);
  auto end_openssl = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_openssl = end_openssl - start_openssl;
  std::cout << "elapsed mod dot of openssl time is " << diff_openssl.count() << " s" << std::endl;

  char *output;
  auto start_cmodel = std::chrono::system_clock::now();
  operation(opcode, count, p_bitcount, input, &output);
  auto end_cmodel = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_cmodel = end_cmodel - start_cmodel;
  std::cout << "elapsed mod dot of cmodel time is " << diff_cmodel.count() << " s" << std::endl;

  int flag = 1;
  for (int i = 0; i < output_size; i++) {
    if (output_openssl[i] != output[i]) {
      std::cout << "mod dot result is wrong!" << std::endl;
      flag = 0;
      break;
    }
  }
  if (flag == 1) {
    std::cout << "mod dot result is correct!" << std::endl;
  }
  std::cout << std::endl;
  delete[] input;
  delete[] output_openssl;
  return;
}

void test_mod_exp_dot(int count, int p_bitcount) {
  int input_size = (2 * count + 2) * BYTECOUNT(p_bitcount) + BYTECOUNT(MON_MUL_BLOCK_LENGTH) + sizeof(short int);
  int output_size = BYTECOUNT(p_bitcount);
  char opcode = 6;
  char *input = new char[input_size];
  generate_test_parameter(opcode, count, p_bitcount, input);

  char *output_openssl = new char[output_size];
  auto start_openssl = std::chrono::system_clock::now();
  compute_with_openssl(opcode, count, p_bitcount, input, output_openssl);
  auto end_openssl = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_openssl = end_openssl - start_openssl;
  std::cout << "elapsed mod exp dot of openssl time is " << diff_openssl.count() << " s" << std::endl;

  char *output;
  auto start_cmodel = std::chrono::system_clock::now();
  operation(opcode, count, p_bitcount, input, &output);
  auto end_cmodel = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_cmodel = end_cmodel - start_cmodel;
  std::cout << "elapsed mod exp dot of cmodel time is " << diff_cmodel.count() << " s" << std::endl;

  int flag = 1;
  for (int i = 0; i < output_size; i++) {
    if (output_openssl[i] != output[i]) {
      std::cout << "mod exp dot result is wrong!" << std::endl;
      flag = 0;
      break;
    }
  }
  if (flag == 1) {
    std::cout << "mod exp dot result is correct!" << std::endl;
  }
  std::cout << std::endl;
  delete[] input;
  delete[] output_openssl;
  return;
}

void test_random_generate(int count, int bitcount, int thread_num) {
  int outbyte = (count * bitcount + 7) / 8;
  unsigned char seedval[16];
  for (int i = 0; i < 16; i++) {
    seedval[i] = rand(); // seedval is random number
  }
  unsigned char *randout = new unsigned char[outbyte];
  unsigned char *opensslout = new unsigned char[outbyte];

  // cmodel random
  auto start_cmodel = std::chrono::system_clock::now();
  generate_pseudorandom_multhread(count, seedval, bitcount, (char*) randout, thread_num);
  auto end_cmodel = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_cmodel = end_cmodel - start_cmodel;
  std::cout << "elapsed random generator of cmodel time is " << diff_cmodel.count() << " s" << std::endl;

  // openssl random
  auto start_openssl = std::chrono::system_clock::now();
  EVP_CIPHER_CTX *ctx =  EVP_CIPHER_CTX_new();
  unsigned char * iv = new unsigned char[16]();
  unsigned char * in = new unsigned char[outbyte]();
  int outl;
  EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, seedval, iv);
  EVP_EncryptUpdate(ctx, opensslout, &outl, in, outbyte);
  delete [] iv;
  delete [] in;
  auto end_openssl = std::chrono::system_clock::now();
  std::chrono::duration<double> diff_openssl = end_openssl - start_openssl;
  std::cout << "elapsed random generator of openssl time is " << diff_openssl.count() << " s" << std::endl;

  // test equal
  int t = memcmp(randout, opensslout, outbyte);
  if(t == 0) {
    std::cout << "get PRNG successfully! " << std::endl;
  } else {
    std::cout << "get PRNG wrong! " << std::endl;
  }
  std::cout << std::endl;
  delete [] opensslout;
  delete [] randout;
  return;
}

void print_valuehex(char *a, int byte_size) {
  for (int i = 0; i < byte_size; ++i) {
    printf("%02x", (unsigned char)a[i]);
  }
}

void hexString2Bytes(string hexStr,  char *data, int len) {
        int idx = 0;
        for (size_t i = 0; i < hexStr.size();) {
            string temp = hexStr.substr(i, 2);
            int hex;
            char str[3];
            memcpy(str, temp.c_str(), 2);
            str[2] = '\0';
            sscanf(str, "%2x", &hex);
            data[idx++] = hex;
            if (idx >= len) {
                break;
            }
            i += 2;
        }
}

void hexStr2Vec(string hexStr, vector<uint8_t> &out){
  int idx = 0;
  for (size_t i = 0; i < hexStr.size();) {
      string temp = hexStr.substr(i, 2);
      int hex;
      char str[3];
      memcpy(str, temp.c_str(), 2);
      str[2] = '\0';
      sscanf(str, "%2x", &hex);
      out.push_back(hex);
      i += 2;
  }
}

void bytes2Vec(char *data, int len, vector<uint8_t> &out)
{
    for(int i =0; i < len; ++i){
        out.push_back(data[i]);
    }
}

void my_test()
{
  std::string a_hex = "387197f23daaf3ad520014e6ba12d355242fc42cee4fb147218d4f83102b4ed9456a8b0ab81cc9fbc05d51a0c6eb9d7d5d81eb77b26ebfcfef6aeeda80b3d540c4689cbdc7eb28aeedaca36018b35ed3652e0e92813a0488a38157b819915b5ff3485731de2433384580a6b35f62312c3ed19ac8412615c16bf6c3d773f6f5227c9a6aef83c1f1c7018c4c1ecff2306d41d28b07deaf61331398ed28c5040da348a4ca71f9992cf25f04cc44d2ae4cdadd5f265c2dd788dce46d80f98616a8c1a53ffed916d011ae2835a4a84c54c1c145b2f78ddd3099b6cea096957953b4cd359b02a3d9968e2109c2f8715ed58124dc36d474bb9bf72379b60ea598a6b23df571ee6f3c4cedef73557e845f16f06d2e971966c89f4d8020956c370dd5f3e4ab14c147788c445c852636a6a122eed2c6fb28fa0ee8c5607bbae83e9cba33df9bf99e988ed0dfc923197e0deb16eb10a7bd8eab09a9b6826a4ec3597dce39e9370f7a58ee3cdd76bfd501788e7a3550251748015fe8c69cd46e1c03bd2627da07a130bc9fedc4ca22871e92d6a4fba4ec3f794bf8e1ee0fcf4e0923c5229e43d1dbd462b848189e5e51190a320863ee1e667bfe9a517978bdaf7c6e322c444f0b2ca423665756e16161f39b5db908271efd16a7d867b3c9f38d552fac75b60151c30b16153205372030f84dab9b82016046c2efcf5997510977f479fdf53048";
  std::string b_hex = "491247d90e6fd0df3b88e66d60532d02108fc14751a1110e478d7f86ba369411c766680b2f2bdef8887ad898013b557e1f7ff41d007368e7ea2e72557519492e2c12e9523c9f90e0f775ffc195247f6d31415f0d8505a62d4ae7c912b673801d5bb0ac2bec01bef52192731794683021bf4114eb32a2610fc86f7312cc05d861f597ebb8facb5e38b8d13dd03a64973a8da86a5d28d4d72b588c5a66546f028e6d63ce76c7ed5a2b43eafca6a9b46f007cb9e31f605f5605d04afc772eab3eb530ff0e15417e7eb30a20e9cbf76b18b07cd6718bb982536d9663c5e68e259b947d8d9078ee8e61922d898103ff495829b1648863ad14b8358aa39160b73ae0932d65c0b76e5ad730f1cf948cb0ce203ebe63c3427edf476e1bc3b1dd34f9f4614850d38c30275c688e54f71f8092aa94a7ba77855400f25ca011cb758cd6f86b66efeef768ca08bb9c0b7a3b1bead208be2ef976e8da762fbef3dc0c75d12c02b4021ea23044b077e84de306d54b23ae56be37447cca9d10731fcef172c17a981e34a89646f77cd04651b8ab16eefe49ab5e53465e410e05790efc02970275ba863d1aaa5f65d3ef8cd30a4445af771be152a758bf4aea8d5f8f58e8f5461002fc77d0ab4863fd91e3186bf04ba6f9174f5cf5e24302cb5496641eab0179cecf374b7d48d71c7aaa0db563b7e41ecf18c23c29c91f6c0de2f57db2c7e6568fcc";
  std::string p_hex = "80f817ba2542fcee936c11e5e32728e0a8bc41e8cae890257362468e7ffa33f8c2acd5acdcdb2368b111ddeffbe8c209c6efb00446841ac27a852e6cae2ec924e28a1b8b7db68b2bf3f8642fa55b4c04b6d6f0479ff3dcc69c26570a969fc20c264de9fcff35af663a6c64546a280dac3df0b7a0354c3f67222a0028eafbe8e914200d8d64d3477bb8bbf5755bb0a07cbda0f034cf4225660434489253f5206812e7c81eeb4c5e013b737c9dedfa081d407a3e44e27bb4191db43f007e676fb22310afa4eee92583f9353864155d4310382fc8bedfe566b97c44e9b881187b393fa89b411d148c1dfa82b9dd765a115ca4b8d5fde96650937b2ab3af13c1a156f9cdf3088eddb627a380006804f1a2ca0748c34652767cdebcc775ec0bfad5dc15d8e9681119d3370ffcf30f2971788ba4796409806595798fcddaa727ba24645064afc2b62a95c587556ef6eb41208c052db4cca4e15fa2f1d2a02cc20950baca228f23424d2e179947670dc44ac0e7515b5a7d4f6c101b82b98e5c81c671950675e44b5cf02b743639f8c26c03ed48d7d5eb6e231dca9f881def0b5e3ea1fac9d217446510a377f9ed4498667a8fc62d2364113ee0ece850888cad4738612dda5345ef9d14cb384f443ce11e104403a28d43a11db6e7cdcf8699cc0f585f988fce1a53d10e60d1b0f3d992c29d12474e60fbf42d8c34219a7b6e9459cc866b";
  std::string r_hex = "0eeec902a476080a46374e0d38019f594c439c5f3ef5540c672db0ba40b75306d73dfb41fea9b25e1aeba6729a8c87302a6bc20002783dc88090b7a30da4588748113f2246cb9c27603b969b0fb2a02ef611604368dc68b04b681b71e39a9d6c7418aa50be6d87c3db701e1e0c541d65814bbf6729baa0f592575d4df6389e948ea531e39baa5bb8b668d062d0b0dbd5543e19e08330d847273f481e3ed897d4ecfd294be4038393885208c2a03466ba9dff31ed6295380bae36edc0c865dda74fa62bc2cde85bba580647bb641605565e5ece39d4c4ab9007ab36d2384e9d2f2c88150b2ecf11f893205e98f63ac7421f9674b5532c7839d40e5db08a4aae68696916ca1dc717481fe1d71dd40151c78a52c00983836cd13da0080a41e112f9994e3fec08d7c7ce311dc0de32dd1edc9c5913a53ea6456a5c7f89fae94bb8e677148216115edc392f7b1a442e19904a77c83ffefff88f92fe86bcce3785bf8206b1bb5547a8220dbb6fb2b4b9ca6fd913927fa3550ad26a8c68afaa8abae9cb19cad3652b9fe4e6b40c37b5aca6071c620d00968fe1228f64192f5c39cf4b49a7f2dafeac8948227a04983d3b46df60b06c857420356873fb3fc629118fbc28535aa8755585b9b5c1d22184c4bbd47cdcd6b7188e2c815c8bc7b3599217e2eff39f8b0bbda0506f4669f3eb9a31bcf7f8dbd23c1671b7e2ac6fd20c5c78e580";
  std::string n_hex = "1e3832b1914742c03f47d9e1959509bd";

  int p_bitcount = 4096;

  char * a[512];
  char * b[512];
  char * p[512];
  char * n[16];
  char * r[512];

  hexString2Bytes(a_hex, (char *)a, 512);
  hexString2Bytes(b_hex, (char *)b, 512);
  hexString2Bytes(p_hex, (char *)p, 512);
  hexString2Bytes(n_hex, (char *)n, 16);
  hexString2Bytes(r_hex, (char *)r, 512);

  mod_mul(1, (char *)a, (char *)b, (char *)p , p_bitcount,(char *)n,(char *)r);

  print_valuehex((char *)a, 512);
  printf("\r\n");

  uint32_t order = 0x01020304;
  printf("\r\n order=");
  print_valuehex((char *)&order, 4);
  printf("\r\n");

}


int test_cmodel() {
  //my_test();

  // test checksum
  // test_checksum();
  // // test_mod_add(counts, bitcount)
  std::cout << "test_mod_add(1, 4096);" << std::endl;
  test_mod_add(1, 4096);
  // test_mod_add(50, 2048);
  // test_mod_add(100, 1024);
  // test_mod_mul(counts, bitcount)
  std::cout << "test_mod_mul(1, 4096)" << std::endl;
  test_mod_mul(1, 4096);
  // test_mod_mul(10, 2048);
  // test_mod_mul(30, 1024);
  // // test_mod_mul_const(counts, bitcount)
  // test_mod_mul_const(5, 4096);
  // test_mod_mul_const(10, 2048);
  // test_mod_mul_const(30, 1024);
  // // test_mod_exp(runs, bitcount)
  // test_mod_exp(1, 1024);
  // test_mod_exp(1, 2048);
  // test_mod_exp(1, 3072);
  std::cout << "test_mod_exp(1, 4096);" << std::endl;
  test_mod_exp(1, 1024);
  // // test_mod_const_exp(runs, bitcount)
  // test_mod_const_exp(1, 1024);
  // test_mod_const_exp(1, 2048);
  // test_mod_const_exp(1, 3072);
  // test_mod_const_exp(1, 4096);
  // // test_mod_dot(vector_length, bitcount)
  // test_mod_dot(5, 4096);
  // test_mod_dot(50, 2048);
  // test_mod_dot(100, 1024);
  // // test_mod_exp_dot(vector_length, bitcount)
  // test_mod_exp_dot(5, 2048);
  // test_mod_exp_dot(10, 1024);
  // test_mod_exp_dot(30, 512);
  // // test_mod_exp_dot(counts, bitcount, thread)
  // test_random_generate(5, 4096, 1);
  // test_random_generate(50, 2048, 2);
  // test_random_generate(100, 1024, 3);

  return 0;
}


void save_vec_data(std::string filename, vector<uint8_t> &data) {
  ofstream outFile(filename, ios::out | ios::binary);
  for(auto &it : data) {
      outFile.write((char*)&it, 1);
  }
  
  outFile.close();
}



void gen_mod_mul()
{
    std::string a_hex = "387197f23daaf3ad520014e6ba12d355242fc42cee4fb147218d4f83102b4ed9456a8b0ab81cc9fbc05d51a0c6eb9d7d5d81eb77b26ebfcfef6aeeda80b3d540c4689cbdc7eb28aeedaca36018b35ed3652e0e92813a0488a38157b819915b5ff3485731de2433384580a6b35f62312c3ed19ac8412615c16bf6c3d773f6f5227c9a6aef83c1f1c7018c4c1ecff2306d41d28b07deaf61331398ed28c5040da348a4ca71f9992cf25f04cc44d2ae4cdadd5f265c2dd788dce46d80f98616a8c1a53ffed916d011ae2835a4a84c54c1c145b2f78ddd3099b6cea096957953b4cd359b02a3d9968e2109c2f8715ed58124dc36d474bb9bf72379b60ea598a6b23df571ee6f3c4cedef73557e845f16f06d2e971966c89f4d8020956c370dd5f3e4ab14c147788c445c852636a6a122eed2c6fb28fa0ee8c5607bbae83e9cba33df9bf99e988ed0dfc923197e0deb16eb10a7bd8eab09a9b6826a4ec3597dce39e9370f7a58ee3cdd76bfd501788e7a3550251748015fe8c69cd46e1c03bd2627da07a130bc9fedc4ca22871e92d6a4fba4ec3f794bf8e1ee0fcf4e0923c5229e43d1dbd462b848189e5e51190a320863ee1e667bfe9a517978bdaf7c6e322c444f0b2ca423665756e16161f39b5db908271efd16a7d867b3c9f38d552fac75b60151c30b16153205372030f84dab9b82016046c2efcf5997510977f479fdf53048";
    std::string b_hex = "491247d90e6fd0df3b88e66d60532d02108fc14751a1110e478d7f86ba369411c766680b2f2bdef8887ad898013b557e1f7ff41d007368e7ea2e72557519492e2c12e9523c9f90e0f775ffc195247f6d31415f0d8505a62d4ae7c912b673801d5bb0ac2bec01bef52192731794683021bf4114eb32a2610fc86f7312cc05d861f597ebb8facb5e38b8d13dd03a64973a8da86a5d28d4d72b588c5a66546f028e6d63ce76c7ed5a2b43eafca6a9b46f007cb9e31f605f5605d04afc772eab3eb530ff0e15417e7eb30a20e9cbf76b18b07cd6718bb982536d9663c5e68e259b947d8d9078ee8e61922d898103ff495829b1648863ad14b8358aa39160b73ae0932d65c0b76e5ad730f1cf948cb0ce203ebe63c3427edf476e1bc3b1dd34f9f4614850d38c30275c688e54f71f8092aa94a7ba77855400f25ca011cb758cd6f86b66efeef768ca08bb9c0b7a3b1bead208be2ef976e8da762fbef3dc0c75d12c02b4021ea23044b077e84de306d54b23ae56be37447cca9d10731fcef172c17a981e34a89646f77cd04651b8ab16eefe49ab5e53465e410e05790efc02970275ba863d1aaa5f65d3ef8cd30a4445af771be152a758bf4aea8d5f8f58e8f5461002fc77d0ab4863fd91e3186bf04ba6f9174f5cf5e24302cb5496641eab0179cecf374b7d48d71c7aaa0db563b7e41ecf18c23c29c91f6c0de2f57db2c7e6568fcc";
    std::string p_hex = "80f817ba2542fcee936c11e5e32728e0a8bc41e8cae890257362468e7ffa33f8c2acd5acdcdb2368b111ddeffbe8c209c6efb00446841ac27a852e6cae2ec924e28a1b8b7db68b2bf3f8642fa55b4c04b6d6f0479ff3dcc69c26570a969fc20c264de9fcff35af663a6c64546a280dac3df0b7a0354c3f67222a0028eafbe8e914200d8d64d3477bb8bbf5755bb0a07cbda0f034cf4225660434489253f5206812e7c81eeb4c5e013b737c9dedfa081d407a3e44e27bb4191db43f007e676fb22310afa4eee92583f9353864155d4310382fc8bedfe566b97c44e9b881187b393fa89b411d148c1dfa82b9dd765a115ca4b8d5fde96650937b2ab3af13c1a156f9cdf3088eddb627a380006804f1a2ca0748c34652767cdebcc775ec0bfad5dc15d8e9681119d3370ffcf30f2971788ba4796409806595798fcddaa727ba24645064afc2b62a95c587556ef6eb41208c052db4cca4e15fa2f1d2a02cc20950baca228f23424d2e179947670dc44ac0e7515b5a7d4f6c101b82b98e5c81c671950675e44b5cf02b743639f8c26c03ed48d7d5eb6e231dca9f881def0b5e3ea1fac9d217446510a377f9ed4498667a8fc62d2364113ee0ece850888cad4738612dda5345ef9d14cb384f443ce11e104403a28d43a11db6e7cdcf8699cc0f585f988fce1a53d10e60d1b0f3d992c29d12474e60fbf42d8c34219a7b6e9459cc866b";
    std::string r_hex = "0eeec902a476080a46374e0d38019f594c439c5f3ef5540c672db0ba40b75306d73dfb41fea9b25e1aeba6729a8c87302a6bc20002783dc88090b7a30da4588748113f2246cb9c27603b969b0fb2a02ef611604368dc68b04b681b71e39a9d6c7418aa50be6d87c3db701e1e0c541d65814bbf6729baa0f592575d4df6389e948ea531e39baa5bb8b668d062d0b0dbd5543e19e08330d847273f481e3ed897d4ecfd294be4038393885208c2a03466ba9dff31ed6295380bae36edc0c865dda74fa62bc2cde85bba580647bb641605565e5ece39d4c4ab9007ab36d2384e9d2f2c88150b2ecf11f893205e98f63ac7421f9674b5532c7839d40e5db08a4aae68696916ca1dc717481fe1d71dd40151c78a52c00983836cd13da0080a41e112f9994e3fec08d7c7ce311dc0de32dd1edc9c5913a53ea6456a5c7f89fae94bb8e677148216115edc392f7b1a442e19904a77c83ffefff88f92fe86bcce3785bf8206b1bb5547a8220dbb6fb2b4b9ca6fd913927fa3550ad26a8c68afaa8abae9cb19cad3652b9fe4e6b40c37b5aca6071c620d00968fe1228f64192f5c39cf4b49a7f2dafeac8948227a04983d3b46df60b06c857420356873fb3fc629118fbc28535aa8755585b9b5c1d22184c4bbd47cdcd6b7188e2c815c8bc7b3599217e2eff39f8b0bbda0506f4669f3eb9a31bcf7f8dbd23c1671b7e2ac6fd20c5c78e580";
    std::string n_hex = "1e3832b1914742c03f47d9e1959509bd";

    int p_bitcount = 4096;

    char * a[512];
    char * b[512];
    char * p[512];
    char * n[16];
    char * r[512];

    hexString2Bytes(a_hex, (char *)a, 512);
    hexString2Bytes(b_hex, (char *)b, 512);
    hexString2Bytes(p_hex, (char *)p, 512);
    hexString2Bytes(n_hex, (char *)n, 16);
    hexString2Bytes(r_hex, (char *)r, 512);


    HomoBlock hb_mod_mul;
    hb_mod_mul.init(CAL_MODE::CAL_MUL_OR_EXP);

    HomoFrame frame_setp;
    vector<uint8_t> number_p;
    vector<uint8_t> number_r;
    vector<uint8_t> number_n;
    hexStr2Vec(p_hex, number_p);
    hexStr2Vec(r_hex, number_r);
    hexStr2Vec(n_hex, number_n);
    number_r.insert(number_r.end(),number_n.begin(),number_n.end());
    frame_setp.init(0x00,OP_CODE::OP_SET,PBitCount::PB_4096,EBitCount::EB_4096,number_p, number_r);

    HomoFrame frame_op;
    vector<uint8_t> number_1;
    vector<uint8_t> number_2;
    hexStr2Vec(a_hex, number_1);
    hexStr2Vec(b_hex, number_2);
    frame_op.init(0x00,OP_CODE::OP_MUL,PBitCount::PB_4096,EBitCount::EB_4096,number_1, number_2);


    hb_mod_mul.add_frame(frame_setp);
    hb_mod_mul.add_frame(frame_op);


    vector<uint8_t> hb_mod_mul_bin = hb_mod_mul.get_raw_data();


    save_vec_data("hb_mod_mul_input.bin",hb_mod_mul_bin);


    // calc result

    mod_mul(1,  (char *)a, (char *)b, (char *)p,p_bitcount, (char *)n, (char *)r);
    vector<uint8_t> result;
    bytes2Vec((char *)a,512, result);
    save_vec_data("hb_mod_mul_result.bin",result);
}

void gen_mod_exp()
{
    std::string a_hex = "387197f23daaf3ad520014e6ba12d355242fc42cee4fb147218d4f83102b4ed9456a8b0ab81cc9fbc05d51a0c6eb9d7d5d81eb77b26ebfcfef6aeeda80b3d540c4689cbdc7eb28aeedaca36018b35ed3652e0e92813a0488a38157b819915b5ff3485731de2433384580a6b35f62312c3ed19ac8412615c16bf6c3d773f6f5227c9a6aef83c1f1c7018c4c1ecff2306d41d28b07deaf61331398ed28c5040da348a4ca71f9992cf25f04cc44d2ae4cdadd5f265c2dd788dce46d80f98616a8c1a53ffed916d011ae2835a4a84c54c1c145b2f78ddd3099b6cea096957953b4cd359b02a3d9968e2109c2f8715ed58124dc36d474bb9bf72379b60ea598a6b23df571ee6f3c4cedef73557e845f16f06d2e971966c89f4d8020956c370dd5f3e4ab14c147788c445c852636a6a122eed2c6fb28fa0ee8c5607bbae83e9cba33df9bf99e988ed0dfc923197e0deb16eb10a7bd8eab09a9b6826a4ec3597dce39e9370f7a58ee3cdd76bfd501788e7a3550251748015fe8c69cd46e1c03bd2627da07a130bc9fedc4ca22871e92d6a4fba4ec3f794bf8e1ee0fcf4e0923c5229e43d1dbd462b848189e5e51190a320863ee1e667bfe9a517978bdaf7c6e322c444f0b2ca423665756e16161f39b5db908271efd16a7d867b3c9f38d552fac75b60151c30b16153205372030f84dab9b82016046c2efcf5997510977f479fdf53048";
    std::string b_hex = "491247d90e6fd0df3b88e66d60532d02108fc14751a1110e478d7f86ba369411c766680b2f2bdef8887ad898013b557e1f7ff41d007368e7ea2e72557519492e2c12e9523c9f90e0f775ffc195247f6d31415f0d8505a62d4ae7c912b673801d5bb0ac2bec01bef52192731794683021bf4114eb32a2610fc86f7312cc05d861f597ebb8facb5e38b8d13dd03a64973a8da86a5d28d4d72b588c5a66546f028e6d63ce76c7ed5a2b43eafca6a9b46f007cb9e31f605f5605d04afc772eab3eb530ff0e15417e7eb30a20e9cbf76b18b07cd6718bb982536d9663c5e68e259b947d8d9078ee8e61922d898103ff495829b1648863ad14b8358aa39160b73ae0932d65c0b76e5ad730f1cf948cb0ce203ebe63c3427edf476e1bc3b1dd34f9f4614850d38c30275c688e54f71f8092aa94a7ba77855400f25ca011cb758cd6f86b66efeef768ca08bb9c0b7a3b1bead208be2ef976e8da762fbef3dc0c75d12c02b4021ea23044b077e84de306d54b23ae56be37447cca9d10731fcef172c17a981e34a89646f77cd04651b8ab16eefe49ab5e53465e410e05790efc02970275ba863d1aaa5f65d3ef8cd30a4445af771be152a758bf4aea8d5f8f58e8f5461002fc77d0ab4863fd91e3186bf04ba6f9174f5cf5e24302cb5496641eab0179cecf374b7d48d71c7aaa0db563b7e41ecf18c23c29c91f6c0de2f57db2c7e6568fcc";
    std::string p_hex = "80f817ba2542fcee936c11e5e32728e0a8bc41e8cae890257362468e7ffa33f8c2acd5acdcdb2368b111ddeffbe8c209c6efb00446841ac27a852e6cae2ec924e28a1b8b7db68b2bf3f8642fa55b4c04b6d6f0479ff3dcc69c26570a969fc20c264de9fcff35af663a6c64546a280dac3df0b7a0354c3f67222a0028eafbe8e914200d8d64d3477bb8bbf5755bb0a07cbda0f034cf4225660434489253f5206812e7c81eeb4c5e013b737c9dedfa081d407a3e44e27bb4191db43f007e676fb22310afa4eee92583f9353864155d4310382fc8bedfe566b97c44e9b881187b393fa89b411d148c1dfa82b9dd765a115ca4b8d5fde96650937b2ab3af13c1a156f9cdf3088eddb627a380006804f1a2ca0748c34652767cdebcc775ec0bfad5dc15d8e9681119d3370ffcf30f2971788ba4796409806595798fcddaa727ba24645064afc2b62a95c587556ef6eb41208c052db4cca4e15fa2f1d2a02cc20950baca228f23424d2e179947670dc44ac0e7515b5a7d4f6c101b82b98e5c81c671950675e44b5cf02b743639f8c26c03ed48d7d5eb6e231dca9f881def0b5e3ea1fac9d217446510a377f9ed4498667a8fc62d2364113ee0ece850888cad4738612dda5345ef9d14cb384f443ce11e104403a28d43a11db6e7cdcf8699cc0f585f988fce1a53d10e60d1b0f3d992c29d12474e60fbf42d8c34219a7b6e9459cc866b";
    std::string r_hex = "0eeec902a476080a46374e0d38019f594c439c5f3ef5540c672db0ba40b75306d73dfb41fea9b25e1aeba6729a8c87302a6bc20002783dc88090b7a30da4588748113f2246cb9c27603b969b0fb2a02ef611604368dc68b04b681b71e39a9d6c7418aa50be6d87c3db701e1e0c541d65814bbf6729baa0f592575d4df6389e948ea531e39baa5bb8b668d062d0b0dbd5543e19e08330d847273f481e3ed897d4ecfd294be4038393885208c2a03466ba9dff31ed6295380bae36edc0c865dda74fa62bc2cde85bba580647bb641605565e5ece39d4c4ab9007ab36d2384e9d2f2c88150b2ecf11f893205e98f63ac7421f9674b5532c7839d40e5db08a4aae68696916ca1dc717481fe1d71dd40151c78a52c00983836cd13da0080a41e112f9994e3fec08d7c7ce311dc0de32dd1edc9c5913a53ea6456a5c7f89fae94bb8e677148216115edc392f7b1a442e19904a77c83ffefff88f92fe86bcce3785bf8206b1bb5547a8220dbb6fb2b4b9ca6fd913927fa3550ad26a8c68afaa8abae9cb19cad3652b9fe4e6b40c37b5aca6071c620d00968fe1228f64192f5c39cf4b49a7f2dafeac8948227a04983d3b46df60b06c857420356873fb3fc629118fbc28535aa8755585b9b5c1d22184c4bbd47cdcd6b7188e2c815c8bc7b3599217e2eff39f8b0bbda0506f4669f3eb9a31bcf7f8dbd23c1671b7e2ac6fd20c5c78e580";
    std::string n_hex = "1e3832b1914742c03f47d9e1959509bd";

    int p_bitcount = 4096;

    char * a[512];
    char * b[512];
    char * p[512];
    char * n[16];
    char * r[512];

    hexString2Bytes(a_hex, (char *)a, 512);
    hexString2Bytes(b_hex, (char *)b, 512);
    hexString2Bytes(p_hex, (char *)p, 512);
    hexString2Bytes(n_hex, (char *)n, 16);
    hexString2Bytes(r_hex, (char *)r, 512);


    HomoBlock hb_mod_mul;
    hb_mod_mul.init(CAL_MODE::CAL_MUL_OR_EXP);

    HomoFrame frame_setp;
    vector<uint8_t> number_p;
    vector<uint8_t> number_r;
    vector<uint8_t> number_n;
    hexStr2Vec(p_hex, number_p);
    hexStr2Vec(r_hex, number_r);
    hexStr2Vec(n_hex, number_n);
    number_r.insert(number_r.end(),number_n.begin(),number_n.end());
    frame_setp.init(0x00,OP_CODE::OP_SET,PBitCount::PB_4096,EBitCount::EB_4096,number_p, number_r);

    HomoFrame frame_op;
    vector<uint8_t> number_1;
    vector<uint8_t> number_2;
    hexStr2Vec(a_hex, number_1);
    hexStr2Vec(b_hex, number_2);
    frame_op.init(0x00,OP_CODE::OP_EXP,PBitCount::PB_4096,EBitCount::EB_4096,number_1, number_2);


    hb_mod_mul.add_frame(frame_setp);
    hb_mod_mul.add_frame(frame_op);


    vector<uint8_t> hb_mod_mul_bin = hb_mod_mul.get_raw_data();


    save_vec_data("hb_mod_exp_input.bin",hb_mod_mul_bin);


    // calc result
    char o[512];
    mod_exp(1,  (char *)a, (char *)b, p_bitcount , (char *)p,p_bitcount, (char *)n, (char *)r, (char *)o);
    vector<uint8_t> result;
    bytes2Vec((char *)o,512, result);
    save_vec_data("hb_mod_exp_result.bin",result);
}

#include <gmp.h>

void test_gmp()
{
    std::string s_p = "96dbe3449dd5a33f8443fd5b49a54f87095593ed828ad6d694e2349c9bc42f76525f793df3fbeaa3d748a5a252546bf1465136e217c10d1244498eb47431f9165faf190ad41603df1d57c4d93bc7c140b32d23ba53a357172d664329204c7db0ecb12cc8dc05cc324a7eac37f7f43a4dee20f8c493f126975ccac63da6ee238e6051e70d2d115834552c40728289988ef2b9f09d90e4015bbfde8c7f4841bce16468b89f705d20b6f7f284aa9294c38af3cffbde1d536bfd13b517951f4d8b753ca568ee52a8e90dbeb8ffc722ed8e3113c4690afc3cae8f27224f53d267b9ad827689aff82af404c57a71723b4891a94f2a63ea72133f51ba2f6c7cf9c7ef96d971d3d70b7513cded61355ba99260c2ed53ac1170d62a2b138a88acab1d18675019744ed627126546067c1286610e19c8cce6bf82f060e4eaee287d45768a04ceb63ef7356dc95ff6b4823619186a9826fc12026b5a64d888211bc415f4870617124c266f4ac4f5a92144a873d668e8d6915c39748a3f4b3602d4322e83f858c96ff97491b24b0417737812a68b9d52648891b1032761987727ba006f33f288293515c683472d8864e8e4b5267d272038e70997819e7f902fdcffb74157a53ba4c856b7166b414226c19ace512e98da8cacfd26fb63fe2ca562aff599343052d788856ecb55fc3c93f298611385839f13b05aae736f803e41b725a6c44eaf6d";
    std::string s_a = "6c98cd0f2976e53f0ff89f39582bf0b0f5dd251adc403793493dbdd63b1ce6b7be3c9b04345deb503dc364c2829abacd8666539fc1bc8b27d5925d6ef8f5cd0c56e037148f391739cc6b09fd1330780eeaee8f65a36b2e1ebce388f22075c84068fb3658df345a6fdf3f04af99c0b8560c1cdc791d096b29a583a162c2411ebd3e795ac09cf0a0baf91aea26ba56ae81b354a814e4476bac6cdd3dacf03787e373edd47814eae13675cff6be4f3133816c40fd8527d874b9305fb76012c9c153ba2ac1834120ee53491410ed450df36cb8003dcb7f3440dd389ffef85a65b51156c2ac256d54b5f1468a99a5130877c6f529053b88940e3edb06512f401cee1406ad2ab3b886df59ebb61a8e8ff048c3b1e7d3a2e71cf7ededdbd5e3d7969aefbfe73b948709ef31a3cbbfa8b6b42de2ae0878107a135666cf36a2b6c8a86fbf042ed34c5dd5587d9b44fa27d3e826b9808822fb5326f74b0b9a7992e67cb9ec57e40684a6d02b26f4c9c598d96414b372142719606e78d9186da1d4608262f83f7f2a680609705e8012af045790d42b323263189d93b744ca2ea17295d3d00a7ebf03aba499cc9e9a188bba13eab2af53989ac0a66b6773c3e0320be4bc58c050adc63c680b4a09da9f850c0a49ef5332859132a55096ab2ff040758a9072792c2c2acd26e280ca9cc833655aac46325267c6c863e714d5d01a889236d95586";
    std::string s_e = "7cf527fedc80e0fc37059fb0bd9dbede89223d29c72b64f1637ca87b9225982dbef3df461a6df45544be6aeef0b50981b8186d64c1bc42cf5d5d62b44ebd3802b09506104485a64a96332def23dae29392b12dafc30f146b52a86f61ae8cf2e6db1181bcd083bb42f93be3a276fa459b44343658c68f61d752f948285e9c8993d0e66d444ae4277bb29079391a55845e00120058cf5afb031c26b7e051bfca708c3c872e57a43375cba6c5b255a97489a05d84d74dc5b53388d244ec4b8951164feb7071ead8b0d91e9e3a0e4ed0f522f17c582e5ee0b55a0bee526a7e5102fceaf049518920b6bb27bc73e899381cbc9288825b409c3cd51b1e1556fe7b611433305b7304e77651b13c8c2f421fb2a6395f0fd5f95fda42a6c1632fd7141a121b5bb35ef74cff2d84c9b9cf9b1ea8de85da6ba5e4b663bbd6296b7a1e0444b5e6275e619527a878b62f4823c1f210299e0c682e13f65ad9d5c4536db51a8c97d0ae168496eb87194963339aaa5d1f6d95cda2ae613772c27b59e28371b316a2f77c3da8eb5e738ba8d754c11158ab9414e3d0297c2b51c83a6bce7c9392cbf95bfb1454eb3c22fcc6c9141d87861f9c151cf31ac429fd2e85cb49b8d7ddcb1915bccbad4329a9b0654121b15eee5067562ee4a71a69c58bc6b404b868f3eede0dc8a44939c996e1994f6b2f2bb27c61da98db633f2ef32e4899ad3b102a3f3e";

    BIGNUM *p = BN_new();
    BIGNUM *a = BN_new();
    BIGNUM *e = BN_new();
    BIGNUM *r = BN_new();

    BN_CTX * ctx = BN_CTX_new();

    BN_hex2bn(&p, s_p.c_str());
    BN_hex2bn(&a, s_a.c_str());
    BN_hex2bn(&e, s_e.c_str());

    BN_mod_exp(r,a,e,p,ctx);
    std::cout << BN_bn2hex(r) << std::endl;


}


int main(int argc, char* argv[])
{

    boost::program_options::options_description opts("homo tools");
    opts.add_options()
            ("help,h","help info")
            ("file,f",boost::program_options::value<string>(),"run a script file, file name ")
            ("test,t","run test")
            ("repl,r","run script repl");

    boost::program_options::variables_map vm;
    try
    {
        store(parse_command_line(argc,argv,opts),vm); // 分析参数
    }
    catch(boost::program_options::error_with_no_option_name &ex)
    {
        cout<<ex.what()<<endl;
    }

    notify(vm); // 将解析的结果存储到外部变量


    if (vm.count("test")) {
        test_cmodel();
    }
    else if (vm.count("file")) {
        HScriptEngine hse;
        std::ifstream t(vm["file"].as<string>());
        std::string str((std::istreambuf_iterator<char>(t)),std::istreambuf_iterator<char>());
        hse.Eval(str);
    }else if(vm.count("repl")) {
        HScriptEngine hse;
        std::cout << "> " ;
        std::string cmd;
        while(true) {
            std::string sub_str;
            cin >> sub_str;
            cmd += " ";
            if(sub_str == "exit") {
                break;
            }
            else if(sub_str.find(')') != std::string::npos) {
                cmd +=  sub_str;
                hse.Eval(cmd);
                std::cout << "> " ;
                cmd = "";
            }else {
                cmd +=  sub_str;
            }
        }

    }else if(vm.count("help")){
        std::string heplinfo =R"(
  脚本采用类lisp风格：
  - 模乘   a1*b1 mod p  ,  a2*b2 mod p
    (!*  p  a1 b1  a2 b2)
  - 模幂   a1^e1 mod p   ,  a2^e2 mod p
    (!^  p  a1 e1  a2 e2)
  - 模乘之和   a1*b1 mod p  +  a2*b2 mod p
    (@*  p  a1 b1  a2 b2)
  - 模幂之积   a1^b1 mod p  *  a2^b2 mod p
    (@^  p  a1 e1  a2 e2)

脚本功能设置：
  - 设置p_bitcount    (set p_bitcount  0x400)
  - 设置e_bitcount    (set e_bitcount  0x400)
  - 保存输入bin文件    (saveblock test)
        )";

        std::cout << heplinfo << std::endl;
    }

    else {
        cout<<opts<<endl;
        return -1;
    }

    return 0;
}